#ifndef LEPTJSON_H__
#define LEPTJSON_H__
#include <string>
#include <memory>
#include <vector>
#include <unordered_map>
enum class lept_type
{
    LEPT_NULL,
    LEPT_FALSE,
    LEPT_TRUE,
    LEPT_NUMBER,
    LEPT_STRING,
    LEPT_ARRAY,
    LEPT_OBJECT
};


enum class lept_error_type
{
    LEPT_PARSE_OK = 0,
    LEPT_PARSE_EXPECT_VALUE,
    LEPT_PARSE_INVALID_VALUE,
    LEPT_PARSE_ROOT_NOT_SINGULAR,
    LEPT_PARSE_NUMBER_TOO_BIG,
    LEPT_PARSE_MISS_QUOTATION_MARK,
    LEPT_PARSE_INVALID_STRING_ESCAPE,
    LEPT_PARSE_INVALID_STRING_CHAR,
    LEPT_PARSE_INVALID_UNICODE_SURROGATE,
    LEPT_PARSE_INVALID_UNICODE_HEX,
    LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET,
    LEPT_PARSE_MISS_KEY,
    LEPT_PARSE_MISS_COLON,
    LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET
};

/* ERROR RELATED */
class NoSuchMemberException : std::exception {};
class UnableStringifyException : std::exception {};

class ILeptJSON
{
public:
    virtual ~ILeptJSON() {}
    virtual lept_type lept_get_type() const =0;
    virtual lept_error_type lept_get_error_type() const=0;
    virtual std::string lept_stringify() const =0;
    virtual bool operator==(const ILeptJSON& _val) const {
        if(_val.lept_get_type()!=this->lept_get_type()){
            return false;
        }
        return true;
    }
    /* template */
    template <class T>
	const T& lept_get_element() const {
        /*有问题，调用时需要指定类型*/
        /* 使用辅助函数，删去调用时的类型指定 */
		throw NoSuchMemberException();
	}
	template <class T>
	T& lept_set_element() {
		throw NoSuchMemberException();
	}
    
private:

    /*get const value */
    virtual const double& get_number() const { throw NoSuchMemberException(); }
    virtual const std::string& get_string() const { throw NoSuchMemberException(); }
    virtual const std::vector<std::unique_ptr<ILeptJSON>>& get_array() const { throw NoSuchMemberException(); }
    virtual const std::unordered_map<std::string,std::unique_ptr<ILeptJSON>>& get_object() const { throw NoSuchMemberException(); }

    /*get value */
    virtual double& get_number()  { throw NoSuchMemberException(); }
    virtual std::string& get_string()  { throw NoSuchMemberException(); }
    virtual std::vector<std::unique_ptr<ILeptJSON>>& get_array() { throw NoSuchMemberException(); }
    virtual std::unordered_map<std::string,std::unique_ptr<ILeptJSON>>& get_object() { throw NoSuchMemberException(); }

};

/* lept_get_element */
template<>
inline const double& ILeptJSON::lept_get_element<double>() const{
    return get_number();
}

template<>
inline const std::string& ILeptJSON::lept_get_element<std::string>() const{
    return get_string();
}

template<>
inline const std::vector<std::unique_ptr<ILeptJSON>>& ILeptJSON::lept_get_element< std::vector<std::unique_ptr<ILeptJSON>>>() const{
    return get_array(); 
}

template<>
inline const std::unordered_map<std::string,std::unique_ptr<ILeptJSON>>& ILeptJSON::lept_get_element< std::unordered_map<std::string,std::unique_ptr<ILeptJSON>> >() const{
    return get_object(); 
}

/* lept_set_element */
template<>
inline double& ILeptJSON::lept_set_element<double>() {
    return get_number();
}

template<>
inline std::string& ILeptJSON::lept_set_element<std::string>() {
    return get_string();
}

template<>
inline std::vector<std::unique_ptr<ILeptJSON>>& ILeptJSON::lept_set_element< std::vector<std::unique_ptr<ILeptJSON>>>(){
    return get_array(); 
}

template<>
inline  std::unordered_map<std::string,std::unique_ptr<ILeptJSON>>& ILeptJSON::lept_set_element<std::unordered_map<std::string,std::unique_ptr<ILeptJSON>>>() {
    return get_object(); 
}

/* Derived Class*/
class LeptError: public ILeptJSON
{
    lept_error_type lept_error;

    lept_type lept_get_type() const override { return lept_type::LEPT_NULL;}
    lept_error_type lept_get_error_type() const override { return lept_error; }
    std::string lept_stringify() const override {throw UnableStringifyException();}
    bool operator==(const ILeptJSON& _val) const override{
        if(_val.lept_get_error_type()!=this->lept_get_error_type()){
            return false;
        }
        return true;
    }
    ~LeptError() {}
public:
    LeptError(lept_error_type _lept_error)
    :lept_error(_lept_error)
        {}

};

class LeptNull: public ILeptJSON
{
    lept_type lept_get_type() const override { return lept_type::LEPT_NULL;}
    lept_error_type lept_get_error_type() const override { return lept_error_type::LEPT_PARSE_OK;}
    std::string lept_stringify() const override {return "null";}
    ~LeptNull() {}
};

class LeptFalse:public ILeptJSON
{
    lept_type lept_get_type() const override{ return lept_type::LEPT_FALSE;}
    lept_error_type lept_get_error_type() const override { return lept_error_type::LEPT_PARSE_OK;}
    std::string lept_stringify() const override {return "false";}
    ~LeptFalse() {}

};

class LeptTrue:public ILeptJSON
{
    lept_type lept_get_type() const override{ return lept_type::LEPT_TRUE;}
    lept_error_type lept_get_error_type() const override{ return  lept_error_type::LEPT_PARSE_OK;}
    std::string lept_stringify() const override {return "true";}
    ~LeptTrue() {}
};


#include<algorithm>
class LeptNumber:public ILeptJSON
{
    double val;
public:
    explicit LeptNumber(double _val):val(_val){}
    LeptNumber(LeptNumber& _val){
        this->val=_val.lept_get_element<double>();
    }
    ~LeptNumber() {}
private:
    lept_type lept_get_type() const override { return lept_type::LEPT_NUMBER;}
    lept_error_type lept_get_error_type() const override{ return lept_error_type::LEPT_PARSE_OK;}
    std::string lept_stringify() const override {
        char str[256];
        sprintf(str,"%.17g",this->val);
        return std::string(str);
    }
    bool operator==(const ILeptJSON& _val) const override{
        if(_val.lept_get_type()!=this->lept_get_type()
        || _val.lept_get_element<double>()!=this->val){
            return false;
        }
        return true;
    }
    const double& get_number() const override {return val;}
    double& get_number() override { return const_cast<double&>(static_cast<const LeptNumber* const>(this)->get_number());}
};
class LeptString:public ILeptJSON
{
    std::string val;
public:
    LeptString(){}
    explicit LeptString(std::string _val):val(_val){}
    LeptString(const LeptString& _val){
        this->val=_val.lept_get_element<std::string>();
    }
    ~LeptString(){}
private:
    lept_type lept_get_type() const override { return lept_type::LEPT_STRING;}
    lept_error_type lept_get_error_type() const override{ return lept_error_type::LEPT_PARSE_OK;}
    std::string lept_stringify() const override {
        std::string str("\"");
        const char hex_digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        for(const auto& c:val){
            switch (c) {
            case '\"':   str+="\\\""; break;
            case '\\':   str+="\\\\"; break;
            case '\b':   str+="\\b"; break;
            case '\f':   str+="\\f"; break;
            case '\n':   str+="\\n"; break;
            case '\r':   str+="\\r"; break;
            case '\t':   str+="\\t"; break;
            default:
                if(c<0x20) {
                    /*对于多字节unicode无法解码*/
#if 1
                    char buffer[7];
                    sprintf(buffer,"\\u%04X",c);
                    str+=buffer;
#else
                    str+="\\u00";
                    str+=hex_digits[c>>4];
                    str+=hex_digits[c & 0xF];
#endif
                }
                else
                    str+=c;
            }
        }
        str+='\"';
        return str;
    }
    bool operator==(const ILeptJSON& _val) const override{
        if(_val.lept_get_type()!=this->lept_get_type()
        || _val.lept_get_element<std::string>()!=this->val){
            return false;
        }
        return true;
    }    
    const std::string& get_string() const override { return val;}
    std::string& get_string() override{
        return const_cast<std::string&>(static_cast<const LeptString* const>(this)->get_string());
    }


};

class LeptArray:public ILeptJSON
{
    typedef std::vector<std::unique_ptr<ILeptJSON>> leptjson_vector;
    leptjson_vector vec;
public:
    LeptArray(){}
    explicit LeptArray(leptjson_vector& _vec){
#if 0
        for(auto& v:_vec){
            vec.emplace_back(std::move(v));
        }
#else
        vec= std::move(_vec);
#endif
    }
    LeptArray(LeptArray& _val){
        vec=std::move(_val.lept_set_element<leptjson_vector>());
    }
    ~LeptArray(){}
private:
    lept_type lept_get_type() const override { return lept_type::LEPT_ARRAY;}
    lept_error_type lept_get_error_type() const override{ return lept_error_type::LEPT_PARSE_OK;}
    std::string lept_stringify() const override {
        std::string str("[");
        for(const auto& v:vec){
            str+=v->lept_stringify();
            str+=',';
        }
        if(str.back()==',') str.pop_back();
        str+=']';
        return str;
    }
    bool operator==(const ILeptJSON& _val) const override{
        if(_val.lept_get_type()!=this->lept_get_type()){
            return false;
        }
        if(vec.size()!=_val.lept_get_element<leptjson_vector>().size()) return false;
        size_t n=vec.size();
        for(int i=0;i<n;i++){
            if(!(*_val.lept_get_element<leptjson_vector>()[i].get()==*vec[i].get())) return false;
        }
        return true;
    }
    const leptjson_vector& get_array() const override { return vec; }
    leptjson_vector& get_array() override {
        return const_cast<leptjson_vector&>(static_cast<const LeptArray* const>(this)->get_array());
    }
};

class LeptObject:public ILeptJSON
{   
    typedef std::unordered_map<std::string,std::unique_ptr<ILeptJSON>> leptjson_umap_type;
    leptjson_umap_type umap;
public:
    LeptObject(){}
    LeptObject(leptjson_umap_type _umap){
        umap=std::move(_umap);
    }
    LeptObject(LeptObject& _val){
        umap=std::move(_val.lept_set_element<leptjson_umap_type>());
    }
    ~LeptObject(){}
private:
    lept_type lept_get_type() const override { return lept_type::LEPT_OBJECT;}
    lept_error_type lept_get_error_type() const override{ return lept_error_type::LEPT_PARSE_OK;}
    std::string lept_stringify() const override {
        std::string str("{");
        for(const auto& v:umap){
            str+=v.first+":";
            str+=v.second->lept_stringify();
            str+=',';
        }
        if(str.back()==',') str.pop_back();
        str+='}';
        return str;
    }
    bool operator==(const ILeptJSON& _val) const override{
        if(_val.lept_get_type()!=this->lept_get_type()){
            return false;
        }
        if(umap.size()!=_val.lept_get_element<leptjson_umap_type>().size()) {
            return false;
        }
        for(const auto& v:umap){
            if(_val.lept_get_element<leptjson_umap_type>().find(v.first)==_val.lept_get_element<leptjson_umap_type>().end()
            || !(*_val.lept_get_element<leptjson_umap_type>().at(v.first).get()==*v.second.get())){
                return false;
            }
        }        
        return true;
    }
    const leptjson_umap_type& get_object() const override { return umap; }
    leptjson_umap_type& get_object() override {
        return const_cast<leptjson_umap_type&>(static_cast<const LeptObject* const>(this)->get_object());
    }
};

class MultiManageLeptJSON{
    /*统一管理所有类*/
private:
    class lept_context{
    public:
        const char* json;
    };
    static void lept_parse_whitespace(lept_context& c);
	static std::unique_ptr<ILeptJSON> lept_parse_literal(lept_context& c,
		std::string const& str);
        
    // static std::string MultiManageLeptJSON::lept_parse_string_raw(lept_context& c);

	static std::unique_ptr<ILeptJSON> lept_parse_string(lept_context& c);
	static std::unique_ptr<ILeptJSON> lept_parse_number(lept_context& c);
	static std::unique_ptr<ILeptJSON> lept_parse_array(lept_context& c);
	static std::unique_ptr<ILeptJSON> lept_parse_object(lept_context& c);
	static std::unique_ptr<ILeptJSON> lept_parse_value(lept_context& c);
public:
    static std::unique_ptr<ILeptJSON> lept_parse(const char* json);
};


#endif /* LEPTJSON_H__ */